package com.cnfangmao.masterdata.service.impl;

import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Field;
import org.springframework.data.mongodb.core.query.Query;

import com.cnfangmao.masterdata.common._enum.MasterDataStoragePeriodEnum;
import com.cnfangmao.masterdata.common._enum.OrderByEnum;
import com.cnfangmao.masterdata.common.constant.Constant;
import com.cnfangmao.masterdata.dto.PageDto;
import com.cnfangmao.masterdata.model.BasicDataFieldMetadata;
import com.cnfangmao.masterdata.model.BasicDataMetadata;
import com.cnfangmao.masterdata.service.AccountBasicDataRightService;
import com.cnfangmao.masterdata.service.BasicDataFieldMetadataService;
import com.cnfangmao.masterdata.service.BasicDataMetadataService;
import com.cnfangmao.masterdata.service.MasterDataManagerService;
import com.cnfangmao.masterdata.storage.BasicDataStorageServiceWithMongodb;
import com.cnfangmao.masterdata.vo.MasterDataListPageVo;
import com.cnfangmao.masterdata.common._enum.IsSelectFieldEnum;

/**    
 * @description: 基于mongodb存储引擎的主数据管理服务基类 ,各个存储阶段的主数据服务类都必须继承该基类
 * @author:	chenxc   
 * @date:   2020年4月22日 下午12:29:45   
 */
public abstract class MasterDataManagerServiceImplWithMongodb implements MasterDataManagerService {

	protected MasterDataStoragePeriodEnum period;
	
	@Autowired
	protected BasicDataStorageServiceWithMongodb<Map<String, Object>> storage;
	
	@Autowired
	protected BasicDataMetadataService metadataService;
	
	@Autowired
	protected BasicDataFieldMetadataService fieldService;
	
	@Autowired
	protected AccountBasicDataRightService rightService;
	/**    
	 * @description: 不分页查询 
	 * @author: chenxc
	 * @date: 2020年4月22日 下午12:30:04
	 * @param metadataId
	 * @param userId
	 * @param filterColumnMap
	 * @param selectColumnMap
	 * @param orderColumnMap
	 * @return   
	 * @see com.cnfangmao.masterdata.service.MasterDataManagerService#find(java.lang.String, java.lang.String, com.cnfangmao.masterdata.dto.PageDto, java.util.Map)   
	 */
	@Override
	public List<Map<String, Object>> find(String metadataId, String userId, Map<String, Object> filterColumnMap, Map<String, IsSelectFieldEnum> selectColumnMap, Map<String, OrderByEnum> orderColumnMap) {
		// TODO Auto-generated method stub
		return null;
	}
	
	/**
	 * 
	 * @description: 分页查询指定元主数据元数据Id的主数据  
	 * @author: chenxc
	 * @date: 2020年4月22日 下午3:25:18
	 * @param metadataId
	 * @param userId
	 * @param page
	 * @param filterColumnMap
	 * @param orderColumnMap
	 * @return      
	 * MasterDataListVo      
	 * @throws
	*/
	@Override
	public MasterDataListPageVo findPage(String metadataId, String userId, PageDto page, Map<String, Object> filterColumnMap, Map<String, IsSelectFieldEnum> selectColumnMap, Map<String, OrderByEnum> orderColumnMap){
		// TODO 此处是否需要判断当前userId是否拥有该主数据的查询权限
		
		// 获取当前主数据的元数据
		BasicDataMetadata metadata = this.metadataService.getById(metadataId);
		if(metadata == null){
			throw new IllegalArgumentException(String.format("找到不到id:%s 的元数据", metadataId));
		}
		
		// 获取字段元数据
		List<BasicDataFieldMetadata> lstField = this.findByFieldMetadataId(metadataId);
		if(lstField == null || lstField.size() == 0){
			throw new IllegalArgumentException(String.format("找到不到id:%s 的字段元数据", metadataId));
		}
		
		Map<String, BasicDataFieldMetadata> mapField = this.getMapFromListBasicDataFieldMetadata(lstField);
		Set<String> lstFieldCode = mapField.keySet();
		// 检查分页合法性
		page = page.check();
		
		String collectionName = metadata.getPhysicsTable();
		switch (this.period) {
		case STAGE:
			// 临时存储区，所有元数据都统一放在t_basicdata_stage
			collectionName = Constant.STAGE_MASTERDATA_STORAGE_TABLENAME;
			break;
		case HISTORY:
			// 历史版本存储区，存储的表名是：已发布存储区表名_history
			collectionName = String.format("%s%s", collectionName, Constant.HISTORY_MASTERDATA_STORAGE_TABLENAME_SUFFIX);
			break;
		default:
			break;
		}
		
		MasterDataListPageVo rs = new MasterDataListPageVo();
	
		// 填充当前页序号
		rs.setPageIndex(page.getIndex());
		
		// 构造辅助参数Map
		Map<String, Object> paramMap = new LinkedHashMap<String, Object>();
		paramMap.put("p_metadata_id", metadataId);
		paramMap.put("p_user_id", userId);
		paramMap.put("p_page_dto", page);
		paramMap.put("p_field_list", lstField);
		
		// ==============获取对应的Query（有特殊要求，则有子类重写实现）==============
		Query query = this.getQuery(filterColumnMap, paramMap);
		if(query == null){
			query = new Query();
		}
		
		// ==============指定查询字段==============
	    Field fields = query.fields();
	    
	    if(selectColumnMap == null || selectColumnMap.size() == 0){
	    	// 没有指定查询的列，则按元数据定义的字段来查询
	    	lstFieldCode.forEach(fields::include);
	    }else{
	    	for(Map.Entry<String, IsSelectFieldEnum> item : selectColumnMap.entrySet()){
	    		String fieldCode = item.getKey();
	    		// TODO 要校验列是否是合法的
	    		if(item.getValue() == IsSelectFieldEnum.YES){
	    			fields.include(fieldCode);
	    		}else{
	    			fields.exclude(fieldCode);
	    		}
	    	}
	    }
	    
	    // 排除默认的_id
	    fields.exclude("_id");
	    
	    // ==============指定排序字段==============
	    if(orderColumnMap != null && orderColumnMap.size() > 0){
	    	List<Order> lstOrder = new LinkedList<Order>();
	    	
	    	for(Map.Entry<String, OrderByEnum> item : orderColumnMap.entrySet()){
	    		String fieldCode = item.getKey();
	    		// TODO 要校验列是否是合法的
	    		if(item.getValue() == OrderByEnum.ASC){
	    			lstOrder.add(Order.asc(fieldCode));
	    		}else{
	    			lstOrder.add(Order.desc(fieldCode));
	    		}
	    	}
	    	
	    	query.with(Sort.by(lstOrder));
	    }
		
		rs.setLstDetail(this.storage.findPage(collectionName, page, query));
	    rs.setRowCount(this.storage.findCount(collectionName, query));
    
		return rs;
	}

	/**    
	 * @description: 按主数据实例id获取当前主数据 
	 * @author: chenxc
	 * @date: 2020年4月22日 下午12:30:04
	 * @param metadataId
	 * @param userId
	 * @param masterDataId
	 * @return   
	 * @see com.cnfangmao.masterdata.service.MasterDataManagerService#findOne(java.lang.String, java.lang.String, java.lang.String)   
	 */
	@Override
	public Map<String, Object> findOne(String metadataId, String userId, String masterDataId) {
		// TODO Auto-generated method stub
		return null;
	}

	/**
	 * @description: 构造与的模糊查询的过滤条件Query，子类可以重写该方法，构造具体的过滤条件 Query
	 * @author: chenxc
	 * @date: 2020年4月24日 上午8:48:50
	 * @param: @param queryColumnMap 查询列的Map对象（key：字段名，value：具体查询值）
	 * @param: @param paramMap 每个调用方法，自己构造的辅助Map对象
	 * @param: @return      
	 * @return: Query      
	 * @throws
	 */
	protected Query getQuery(Map<String, Object> filterColumnMap, Map<String, Object> paramMap){
		return BasicDataStorageServiceWithMongodb.getAndQuery(filterColumnMap);
	}
	
	/**
	 * @description: 校验查询列（有带查询类型前缀）是否合法 
	 * @author: chenxc
	 * @date: 2020年4月24日 上午8:52:40
	 * @param: @param lstFieldCode 合法字段集合
	 * @param: @param sourceKey 待校验查询列
	 * @param: @return      
	 * @return: boolean      
	 * @throws
	 */
	protected boolean checkValidQueryField(Set<String> lstFieldCode, String sourceKey){
		if(lstFieldCode == null || lstFieldCode.size() == 0){
			return false;
		}
		
		String key = "";
		
		for(String item : Constant.LIST_QUERY_CONDITION_PREFIX){
			if(sourceKey.startsWith(item)){
				key = sourceKey.substring(item.length());
				break;
			}
		}
		
		return lstFieldCode.contains(key);
	}
	
	/**
	 * @description: 根据查询类型，构造对应的Criteria 
	 * @author: chenxc
	 * @date: 2020年4月24日 上午9:18:52
	 * @param: @param sourceKey
	 * @param: @param targetKeyPrefix
	 * @param: @param targetKeySuffix
	 * @param: @param value
	 * @param: @return      
	 * @return: Criteria      
	 * @throws
	 */
	protected Criteria getCriteria(String sourceKey, String targetKeyPrefix, String targetKeySuffix, Object value){
		Criteria criteria = new Criteria();
		
		if(sourceKey.startsWith(Constant.QUERY_CONDITION_PREFIX_LIKE)){
			String key = sourceKey.substring(Constant.QUERY_CONDITION_PREFIX_LIKE.length());
			return this.getCriteria__like_(key, targetKeyPrefix, targetKeySuffix, value);
		}
		
		if(sourceKey.startsWith(Constant.QUERY_CONDITION_PREFIX_IS)){
			String key = sourceKey.substring(Constant.QUERY_CONDITION_PREFIX_IS.length());
			return this.getCriteria__is_(key, targetKeyPrefix, targetKeySuffix, value);
		}
		
		if(sourceKey.startsWith(Constant.QUERY_CONDITION_PREFIX_GT)){
			String key = sourceKey.substring(Constant.QUERY_CONDITION_PREFIX_GT.length());
			return this.getCriteria__gt_(key, targetKeyPrefix, targetKeySuffix, value);
		}
		
		if(sourceKey.startsWith(Constant.QUERY_CONDITION_PREFIX_GTE)){
			String key = sourceKey.substring(Constant.QUERY_CONDITION_PREFIX_GTE.length());
			return this.getCriteria__gte_(key, targetKeyPrefix, targetKeySuffix, value);
		}
		
		if(sourceKey.startsWith(Constant.QUERY_CONDITION_PREFIX_LT)){
			String key = sourceKey.substring(Constant.QUERY_CONDITION_PREFIX_LT.length());
			return this.getCriteria__lt_(key, targetKeyPrefix, targetKeySuffix, value);
		}
		
		if(sourceKey.startsWith(Constant.QUERY_CONDITION_PREFIX_LTE)){
			String key = sourceKey.substring(Constant.QUERY_CONDITION_PREFIX_LTE.length());
			return this.getCriteria__lte_(key, targetKeyPrefix, targetKeySuffix, value);
		}
		
		// TODO 考虑通过反射机制动态执行响应的方法
//		for(String item : Constant.LIST_QUERY_CONDITION_PREFIX){
//			if(sourceKey.startsWith(item)){
//				String key = item.substring(item.length());
//				Class classZ= this.getClass();
//		        Method method = classZ.getMethod(String.format("getCriteria%s", item));
//		        method.invoke(classZ, key, targetKeyPrefix, targetKeySuffix, value);
//				break;
//			}
//		}
		
		return criteria;
	}
	
	/**
	 * @description: 根据元数据id，获取对应的字段元数据 
	 * @author: chenxc
	 * @date: 2020年4月24日 上午9:36:25
	 * @param: @param metadataId
	 * @param: @return      
	 * @return: List<BasicDataFieldMetadata>      
	 * @throws
	 */
	protected List<BasicDataFieldMetadata> findByFieldMetadataId(String metadataId) {
		return this.fieldService.findByFieldMetadataId(metadataId);
	}
	
	/** 
	 * @description: 将List<BasicDataFieldMetadata>转换成Map，key是字段的code
	 * @author: chenxc
	 * @date: 2020年4月24日 上午10:47:38
	 * @param: @param lstFieldMetadata
	 * @param: @return      
	 * @return: Map<String,BasicDataFieldMetadata>      
	 * @throws
	 */
	protected Map<String, BasicDataFieldMetadata> getMapFromListBasicDataFieldMetadata(List<BasicDataFieldMetadata> lstFieldMetadata) {
		Map<String, BasicDataFieldMetadata> mapField = new LinkedHashMap<String, BasicDataFieldMetadata>();
		for(BasicDataFieldMetadata fieldMetadata : lstFieldMetadata){
			mapField.put(fieldMetadata.getFieldCode(), fieldMetadata);
		}
		
		return mapField;
	}
	
	// 模糊查询
 	private Criteria getCriteria__like_(String key, String targetKeyPrefix, String targetKeySuffix, Object value){
		// 不区分大小写
		Pattern pattern = Pattern.compile(String.format("^.*" + value + ".*$"), Pattern.CASE_INSENSITIVE);
		String targetKey = String.format("%s%s%s", targetKeyPrefix, key, targetKeySuffix);
		return Criteria.where(targetKey).regex(pattern);
	}
	
	// 等于查询
	private Criteria getCriteria__is_(String key, String targetKeyPrefix, String targetKeySuffix, Object value){
		String targetKey = String.format("%s%s%s", targetKeyPrefix, key, targetKeySuffix);
		return Criteria.where(targetKey).is(value);
	}
	
	// 大于查询
	private Criteria getCriteria__gt_(String key, String targetKeyPrefix, String targetKeySuffix, Object value){
		String targetKey = String.format("%s%s%s", targetKeyPrefix, key, targetKeySuffix);
		return Criteria.where(targetKey).gt(value);
	}
	
	// 大于等于
	private Criteria getCriteria__gte_(String key, String targetKeyPrefix, String targetKeySuffix, Object value){
		String targetKey = String.format("%s%s%s", targetKeyPrefix, key, targetKeySuffix);
		return Criteria.where(targetKey).gte(value);
	}
	
	// 小于
	private Criteria getCriteria__lt_(String key, String targetKeyPrefix, String targetKeySuffix, Object value){
		String targetKey = String.format("%s%s%s", targetKeyPrefix, key, targetKeySuffix);
		return Criteria.where(targetKey).lt(value);
	}
	
	// 小于等于
	private Criteria getCriteria__lte_(String key, String targetKeyPrefix, String targetKeySuffix, Object value){
		String targetKey = String.format("%s%s%s", targetKeyPrefix, key, targetKeySuffix);
		return Criteria.where(targetKey).lte(value);
	}
}
